/* Copyright 2007 dnAnalytics Project.
 *
 * Contributors to this file:
 * Patrick van der Velde
 *
 * This file is part of dnAnalytics.  dnAnalytics is licensed under the 
 * Microsoft Public License. See License.txt for a complete copy of the
 * license.
 */

/*
 * Useful books and links:
 * http://docs.sun.com/source/806-3568/ncg_goldberg.html#689 - What every computer scientist should know about floating-point arithmetic
 */

/*
 * Provide methods that compare two doubles / floats and inform the user if addition / substraction / multiplication / division would lead to a loss of precision
 */
using System;
using System.Diagnostics;
using System.Collections.Generic;

namespace dnAnalytics.Math
{
    // Rename to CompareFloats
    // Define sub classes for:
    // Comparing doubles (DoubleComparer) --> Have single default instance.
    //           singles (SingleComparer) --> Have single default instance.
        
    /// <summary>
    /// Utilities for working with floating point numbers.
    /// </summary>
    public static class FloatingPointUtilities
    {
        #region Internal class - DoubleComparer
        
        /// <summary>
        /// An <c>IComparer</c> used to compare double precision floating points based
        /// on either the ULPs (Units in Last Place) or the number of significant digits.
        /// </summary>
        private sealed class DoubleComparer : IComparer<double>
        {
            /// <summary>
            /// The number of ULPs for the comparison. Set to -1 if not in use.
            /// </summary>
            private long m_Ulps = -1;

            /// <summary>
            /// The number of significant digits used for the comparison. Set to -1 if not in use.
            /// </summary>
            private int m_NumberOfSignificantDigits = -1;

            /// <summary>
            /// Creates a new <c>DoubleComparer</c> which will compare floating points
            /// based on the number of ULPs (Units in Last Place).
            /// </summary>
            /// <param name="ulps">
            ///   The number of ULPs used in the comparison
            /// </param>
            public DoubleComparer(long ulps)
            {
                if (ulps < 0)
                {
                    throw new ArgumentOutOfRangeException("ulps");
                }

                m_Ulps = ulps;
            }

            /// <summary>
            /// Creates a new <c>DoubleComparer</c> which will compare floating points
            /// based on the number of significant digits.
            /// </summary>
            /// <param name="numberOfSignificantDigits">
            ///   The number of significant digits used in the comparison.
            /// </param>
            public DoubleComparer(int numberOfSignificantDigits)
            {
                if (numberOfSignificantDigits < 1)
                {
                    throw new ArgumentOutOfRangeException("numberOfSignificantDigits");
                }

                m_NumberOfSignificantDigits = numberOfSignificantDigits;
            }

            /// <summary>
            /// Gets or sets the number of ULPs (Units in Last Place) with which to compare
            /// floating point values.
            /// </summary>
            public long Ulps
            {
                get
                {
                    return m_Ulps;
                }
                set
                {
                    if (value < 0)
                    {
                        throw new ArgumentOutOfRangeException("value");
                    }

                    // Reset the significant digits number
                    m_NumberOfSignificantDigits = -1;
                    // Store the ulps
                    m_Ulps = value;
                }
            }

            /// <summary>
            /// Gets or sets the number of significant digits with which to compare
            /// floating point values.
            /// </summary>
            public int NumberOfSignificantDigits
            {
                get
                {
                    return m_NumberOfSignificantDigits;
                }
                set
                {
                    if (value < 1)
                    {
                        throw new ArgumentOutOfRangeException("value");
                    }

                    // Reset the ulps number
                    m_Ulps = -1;
                    // Store the number of significant digits.
                    m_NumberOfSignificantDigits = value;
                }
            }

            public int Compare(double x, double y)
            {
                return (m_Ulps > -1) ? CompareWithTolerance(x, y, m_Ulps)
                    : CompareWithSignificantFigures(x, y, m_NumberOfSignificantDigits);
            }
        } 
    #endregion

        #region Constants

        /// <summary>
        /// The base number for binary values
        /// </summary>
        private const int smBinaryBaseNumber = 2;

        /// <summary>
        /// The number of binary digits used to represent the binary number for a double precision floating
        /// point value. i.e. there are this many digits used to represent the
        /// actual number, where in a number as: 0.134556 * 10^5 the digits are 0.134556 and the exponent is 5.
        /// </summary>
        private const int smDoublePrecision = 53;

        /// <summary>
        /// The number of binary digits used to represent the binary number for a single precision floating
        /// point value. i.e. there are this many digits used to represent the
        /// actual number, where in a number as: 0.134556 * 10^5 the digits are 0.134556 and the exponent is 5.
        /// </summary>
        private const int smSinglePrecision = 24; 
        
        #endregion

        #region Fields
        
        /// <summary>
        /// The maximum relative precision of a double
        /// </summary>
        private static readonly double smDoubleMachinePrecision = System.Math.Pow(smBinaryBaseNumber, -smDoublePrecision);

        /// <summary>
        /// The maximum relative precision of a single
        /// </summary>
        private static readonly double smSingleMachinePrecision = System.Math.Pow(smBinaryBaseNumber, -smSinglePrecision);

        /// <summary>
        /// The number of significant figures that a double-precision floating point has.
        /// </summary>
        private static readonly int smNumberOfSignificantFiguresForDoubles;

        /// <summary>
        /// The number of significant figures that a single-precision floating point has.
        /// </summary>
        private static readonly int smNumberOfSignificantFiguresForFloats; 

        #endregion

        /// <summary>
        /// Initializes the <see cref="FloatingPointUtilities"/> class.
        /// </summary>
        static FloatingPointUtilities()
        {
            smNumberOfSignificantFiguresForFloats = (int)System.Math.Ceiling(System.Math.Abs(System.Math.Log10(smSingleMachinePrecision)));
            smNumberOfSignificantFiguresForDoubles = (int)System.Math.Ceiling(System.Math.Abs(System.Math.Log10(smDoubleMachinePrecision)));
        }

        /// <summary>
        /// Gets the number of significant figures for floats.
        /// </summary>
        /// <value>The number of significant figures for floats.</value>
        public static int NumberOfSignificantFiguresForFloats
        {
            get
            {
                return smNumberOfSignificantFiguresForFloats;
            }
        }

        /// <summary>
        /// Gets the number of significant figures for doubles.
        /// </summary>
        /// <value>The number of significant figures for doubles.</value>
        public static int NumberOfSignificantFiguresForDoubles
        {
            get
            {
                return smNumberOfSignificantFiguresForDoubles;
            }
        }

        /// <summary>
        /// Returns the magnitude of the number.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns></returns>
        public static int Magnitude(double value)
        {
            // Can't do this with zero because the 10-log of zero doesn't exist.
            if (value.Equals(0.0))
            {
                return 0;
            }

            double magnitude = System.Math.Log10(value);
            // To get the right number we need to know if the value is negative or positive
            // truncating a positive number will always give use the correct magnitude
            // truncating a negative number will give us a magnitude that is off by 1
            if (magnitude < 0)
            {
                return ((int)(System.Math.Truncate(magnitude - 1)));
            }
            return ((int)(System.Math.Truncate(magnitude)));
        }

        /// <summary>
        /// Returns the number divided by it's magnitude, effectively returning a number between 0 and 10.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns></returns>
        public static double Value(double value)
        {
            if (value.Equals(0.0))
            {
                return value;
            }

            int magnitude = Magnitude(value);
            return value * (System.Math.Pow(10, -magnitude));
        }

        /// <summary>
        /// Determines the range of floating point numbers that will match the specified value with the given tolerance.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <param name="ulpsDifference">The ulps difference.</param>
        /// <param name="topRangeEnd">The top range end.</param>
        /// <param name="bottomRangeEnd">The bottom range end.</param>
        public static void RangeOfMatchingFloatingPointNumbers(double value, long ulpsDifference, out double bottomRangeEnd, out double topRangeEnd)
        {
            // Make sure ulpDifference is non-negative
            if (ulpsDifference < 1)
            {
                throw new ArgumentOutOfRangeException("ulpsDifference");
            }

            // If the value is infinity (positive or negative) just
            // return the same infinity for the range.
            if ((double.IsInfinity(value)))
            {
                topRangeEnd = value;
                bottomRangeEnd = value;
                return;
            }

            // If the value is a NaN then the range is a NaN too.
            if (double.IsNaN(value))
            {
                topRangeEnd = double.NaN;
                bottomRangeEnd = double.NaN;
                return;
            }

            long intValue = GetLongFromDouble(value);
            if (intValue < 0)
            {
                if (System.Math.Abs(long.MinValue - intValue) < ulpsDifference)
                {
                    topRangeEnd = BitConverter.Int64BitsToDouble(ulpsDifference + (long.MinValue - intValue));
                }
                else
                {
                    topRangeEnd = BitConverter.Int64BitsToDouble(intValue - ulpsDifference);
                }

                if (System.Math.Abs(intValue) < ulpsDifference)
                {
                    // Under flow. Return ...???
                    // The number would turn positive, which is weird because we're subtracting
                    // That can't be right so return -Double.MaxValue
                    bottomRangeEnd = -double.MaxValue;
                }
                else
                {
                    // intValue is negative. Adding the positive ulpsDifference means that it gets less negative.
                    // However due to the conversion way this means that the actual double value gets more negative :-S
                    bottomRangeEnd = BitConverter.Int64BitsToDouble(intValue + ulpsDifference);
                }
            }
            else
            {
                // IntValue is positive
                if (long.MaxValue - intValue < ulpsDifference)
                {
                    // we have an overflow
                    // In this case we just return double.Max because that is what the long.MaxValue actually converts to.
                    topRangeEnd = double.MaxValue;
                }
                else
                {
                    topRangeEnd = BitConverter.Int64BitsToDouble(intValue + ulpsDifference);
                }

                // Check the bottom range end for underflows
                if (intValue > ulpsDifference)
                {
                    bottomRangeEnd = BitConverter.Int64BitsToDouble(intValue - ulpsDifference);
                }
                else
                {
                    // Int value is bigger than zero but smaller than the ulpsDifference. So we'll need to deal with
                    // the reversal at the negative end
                    bottomRangeEnd = BitConverter.Int64BitsToDouble(long.MinValue + (ulpsDifference - intValue));
                }
            }
        }

        /// <summary>
        /// Returns the floating point number that will match the value with the tolerance on the maximum size (i.e. the result is
        /// always bigger than the value)
        /// </summary>
        /// <param name="value">The value.</param>
        /// <param name="ulpsDifference">The ulps difference.</param>
        /// <returns></returns>
        public static double MaximumMatchingFloatingPointNumber(double value, long ulpsDifference)
        {
            double topRangeEnd, bottomRangeEnd;
            RangeOfMatchingFloatingPointNumbers(value, ulpsDifference, out bottomRangeEnd, out topRangeEnd);
            return topRangeEnd;
        }

        /// <summary>
        /// Returns the floating point number that will match the value with the tolerance on the minimum size (i.e. the result is
        /// always smaller than the value)
        /// </summary>
        /// <param name="value">The value.</param>
        /// <param name="ulpsDifference">The ulps difference.</param>
        /// <returns></returns>
        public static double MinimumMatchingFloatingPointNumber(double value, long ulpsDifference)
        {
            double topRangeEnd, bottomRangeEnd;
            RangeOfMatchingFloatingPointNumbers(value, ulpsDifference, out bottomRangeEnd, out topRangeEnd);
            return bottomRangeEnd;
        }

        /// <summary>
        /// Determines the range of ulps that will match the specified value with the given tolerance.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <param name="relativeDifference">The relative difference.</param>
        /// <param name="topRangeEnd">The number of ULPS between the <c>value</c> and the <c>value + relativeDifference</c>.</param>
        /// <param name="bottomRangeEnd">The number of ULPS between the <c>value</c> and the <c>value - relativeDifference</c>.</param>
        public static void RangeOfMatchingUlps(double value, double relativeDifference, out long bottomRangeEnd, out long topRangeEnd)
        {
            // Make sure the relative is non-negative 
            if (relativeDifference < 0)
            {
                throw new ArgumentOutOfRangeException("relativeDifference");
            }

            // If the value is infinity (positive or negative) then
            // we can't determine the range.
            if ((double.IsInfinity(value)))
            {
                throw new ArgumentOutOfRangeException();
            }

            // If the value is a NaN then we can't determine the range.
            if (double.IsNaN(value))
            {
                throw new ArgumentOutOfRangeException();
            }

            // If the value is zero (0.0) then we can't calculate the relative difference
            // so return the ulps counts for the difference.
            if (value.Equals(0))
            {
                topRangeEnd = GetLongFromDouble(relativeDifference);
                bottomRangeEnd = topRangeEnd;
                return;
            }

            // Calculate the ulps for the maximum and minimum values
            // Note that these can overflow
            long max = GetDirectionalLongFromDouble(value + relativeDifference * System.Math.Abs(value));
            long min = GetDirectionalLongFromDouble(value - relativeDifference * System.Math.Abs(value));

            // Calculate the ulps from the value
            long intValue = GetDirectionalLongFromDouble(value);

            // Determine the ranges
            topRangeEnd = System.Math.Abs(max - intValue);
            bottomRangeEnd = System.Math.Abs(intValue - min);
        }

        /// <summary>
        /// Compares two doubles and determines if they are equal to within the tolerance or not. Equality comparision is based on the binary representation.
        /// </summary>
        /// <remarks>
        /// <para>
        /// The comparision method used is explained in http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm . The article
        /// at http://www.extremeoptimization.com/resources/Articles/FPDotNetConceptsAndFormats.aspx explains how to transform the C code to 
        /// .NET enabled code without using pointers and unsafe code.
        /// </para>
        /// </remarks>
        /// <param name="first">The first value.</param>
        /// <param name="second">The second value.</param>
        /// <param name="maxUlps">The maximum error in terms of Units in Last Place (ulps), i.e. the maximum number of decimals that may be different. Must be 1 or larger.</param>
        /// <returns><see langword="true" /> if both doubles are equal to each other within the specified tolerance; otherwise <see langword="false" />.</returns>
        public static bool EqualsWithTolerance(double first, double second, long maxUlps)
        {
            // Make sure maxUlps is non-negative and small enough that the
            // default NAN won't compare as equal to anything.
            if (maxUlps < 1)
            {
                throw new ArgumentOutOfRangeException("maxUlps");
            }

            // If A or B are infinity (positive or negative) then
            // only return true if they are exactly equal to each other -
            // that is, if they are both infinities of the same sign.
            if ((double.IsInfinity(first)) || (double.IsInfinity(second)))
            {
                return (first == second);
            }

            // If A or B are a NAN, return false. NANs are equal to nothing,
            // not even themselves.
            if (double.IsNaN(first) || double.IsNaN(second))
            {
                return false;
            }

            // Get the first double and convert it to an integer value (by using the binary representation)
            long firstUlong = GetDirectionalLongFromDouble(first);

            // Get the second double and convert it to an integer value (by using the binary representation)
            long secondUlong = GetDirectionalLongFromDouble(second);

            // Now compare the values. 
            // Note that this comparison can overflow so we'll approach this differently
            // Do note that we could overflow this way too. We should probably check that we don't.
            return (first > second) ? (secondUlong + maxUlps >= firstUlong) : (firstUlong + maxUlps >= secondUlong);
        }

        private static long GetLongFromDouble(double value)
        {
            return BitConverter.DoubleToInt64Bits(value);
        }

        private static long GetDirectionalLongFromDouble(double value)
        {
            // Convert in the normal way.
            long result = GetLongFromDouble(value);
            // Now find out where we're at in the range
            // If the value is larger/equal to zero then we can just return the value
            // if the value is negative we substract long.MinValue from it.
            return (result >= 0) ? result : (long.MinValue - result);
        }

        /// <summary>
        /// Compares two doubles and determines if they are equal to within the specified number of significant figures or not. If the numbers
        /// are very close to zero an absolute difference is compared, otherwise the relative difference is compared.
        /// </summary>
        /// <remarks>
        /// <para>
        /// The comparision method used is explained in http://www.cprogramming.com/tutorial/floating_point/understanding_floating_point_representation.html. 
        /// </para>
        /// <para>
        /// The values are equal if the difference between the two numbers is smaller than 10^(-numberOfSignificantFigures). We divide by 
        /// two so that we have half the range on each side of the numbers, e.g. if significantFigures == 2, then 0.01 will equal between 
        /// 0.005 and 0.015, but not 0.02 and not 0.00
        /// </para>
        /// </remarks>
        /// <param name="first">The first value.</param>
        /// <param name="second">The second value.</param>
        /// <param name="numberOfSignificantFigures">The number of significant figures.</param>
        /// <returns><see langword="true" /> if both doubles are equal to each other within the specified number of significant figures; otherwise <see langword="false" />.</returns>
        public static bool EqualsWithinSignificantFigures(double first, double second, int numberOfSignificantFigures)
        {
            // If A or B are a NAN, return false. NANs are equal to nothing,
            // not even themselves.
            if ((double.IsNaN(first)) || (double.IsNaN(second)))
            {
                return false;
            }

            // If A or B are infinity (positive or negative) then
            // only return true if they are exactly equal to each other -
            // that is, if they are both infinities of the same sign.
            if ((double.IsInfinity(first)) || (double.IsInfinity(second)))
            {
                return (first == second);
            }

            if (numberOfSignificantFigures <= 0)
            {
                // Can't have a negative number of significant figures
                throw new ArgumentOutOfRangeException("numberOfSignificantFigures");
            }

            // If both numbers are equal, get out now. This should remove the possibility of both numbers being zero
            // and any problems associated with that.
            if (first.Equals(second))
            {
                return true;
            }

            // If the numbers are very close to zero, use absolute comparison, otherwise use relative comparison
            if ((System.Math.Abs(first) < (10 * smDoubleMachinePrecision)) || (System.Math.Abs(second) < (10 * smDoubleMachinePrecision)))
            {
                return EqualsWithinAbsoluteSignificantFigures(first, second, numberOfSignificantFigures);
            }
            return EqualsWithinRelativeSignificantFigures(first, second, numberOfSignificantFigures);
        }

        /// <summary>
        /// Compares two doubles and determines if they are equal to within the specified number of significant figures or not. 
        /// </summary>
        /// <remarks>
        /// <para>
        /// The comparision method used is explained in http://www.cprogramming.com/tutorial/floating_point/understanding_floating_point_representation.html. 
        /// </para>
        /// <para>
        /// The values are equal if the difference between the two numbers is smaller than 10^(-numberOfSignificantFigures). We divide by 
        /// two so that we have half the range on each side of the numbers, e.g. if significantFigures == 2, then 0.01 will equal between 
        /// 0.005 and 0.015, but not 0.02 and not 0.00
        /// </para>
        /// </remarks>
        /// <param name="first">The first value.</param>
        /// <param name="second">The second value.</param>
        /// <param name="numberOfSignificantFigures">The number of significant figures.</param>
        /// <returns><see langword="true" /> if both doubles are equal to each other within the specified number of significant figures; otherwise <see langword="false" />.</returns>
        private static bool EqualsWithinRelativeSignificantFigures(double first, double second, int numberOfSignificantFigures)
        {
            // If the magnitudes of the two numbers are equal to within one magnitude the numbers could potentially be equal
            int magnitudeOfFirst = Magnitude(first);
            int magnitudeOfSecond = Magnitude(second);
            if (System.Math.Max(magnitudeOfFirst, magnitudeOfSecond) > (System.Math.Min(magnitudeOfFirst, magnitudeOfSecond) + 1))
            {
                return false;
            }

            // Get the power of the number of significantFigures
            // Note that we substract 1 from the value because in the comparison below we don't count the value in front of the
            // decimal point as a significant digit, while it should be.
            double significantFigureMagnitude = System.Math.Pow(10, -(numberOfSignificantFigures - 1));

            // The values are equal if the difference between the two numbers is smaller than
            // 10^(-numberOfSignificantFigures). We divide by two so that we have half the range
            // on each side of the numbers, e.g. if significantFigures == 2, 
            // then 0.01 will equal between 0.005 and 0.015, but not 0.02 and not 0.00
            return (System.Math.Abs((first - second) * System.Math.Pow(10, -System.Math.Max(magnitudeOfFirst, magnitudeOfSecond))) < significantFigureMagnitude / 2.0);
        }

        /// <summary>
        /// Compares two doubles and determines if they are equal to within the specified number of significant figures or not, using the 
        /// number of significant figures as an absolute measure.
        /// </summary>
        /// <remarks>
        /// <para>
        /// The comparision method used is explained in http://www.cprogramming.com/tutorial/floating_point/understanding_floating_point_representation.html. 
        /// </para>
        /// <para>
        /// The values are equal if the difference between the two numbers is smaller than 10^(-numberOfSignificantFigures). We divide by 
        /// two so that we have half the range on each side of the numbers, e.g. if significantFigures == 2, then 0.01 will equal between 
        /// 0.005 and 0.015, but not 0.02 and not 0.00
        /// </para>
        /// </remarks>
        /// <param name="first">The first value.</param>
        /// <param name="second">The second value.</param>
        /// <param name="numberOfSignificantFigures">The number of significant figures.</param>
        /// <returns><see langword="true" /> if both doubles are equal to each other within the specified number of significant figures; otherwise <see langword="false" />.</returns>
        private static bool EqualsWithinAbsoluteSignificantFigures(double first, double second, int numberOfSignificantFigures)
        {
            // Note that we substract 1 from the value because in the comparison below we don't count the value in front of the
            // decimal point as a significant digit, while it should be.
            double significantFigureMagnitude = System.Math.Pow(10, -(numberOfSignificantFigures - 1));

            // The values are equal if the difference between the two numbers is smaller than
            // 10^(-numberOfSignificantFigures). We divide by two so that we have half the range
            // on each side of the numbers, e.g. if significantFigures == 2, 
            // then 0.01 will equal between 0.005 and 0.015, but not 0.02 and not 0.00
            return (System.Math.Abs((first - second)) < significantFigureMagnitude / 2.0);
        }

        /// <summary>
        /// Compares two doubles and determines which double is bigger.
        /// </summary>
        /// <param name="first">The first value.</param>
        /// <param name="second">The second value.</param>
        /// <param name="maxUlps">The maximum error in terms of Units in Last Place (ulps), i.e. the maximum number of decimals that may be different. Must be 1 or larger.</param>
        /// <returns>
        /// <list type="table">
        ///     <listheader>
        ///         <term>Return value</term>
        ///         <description>Meaning</description>
        ///     </listheader>
        ///     <item>
        ///         <term>-1</term>
        ///         <description><paramref name="first"/> is smaller than <paramref name="second"/> by more than the <paramref name="maxUlps"/> tolerance.</description>
        ///     </item>
        ///     <item>
        ///         <term>0</term>
        ///         <description><paramref name="first"/> is equal to <paramref name="second"/> within the <paramref name="maxUlps"/> tolerance.</description>
        ///     </item>
        ///     <item>
        ///         <term>1</term>
        ///         <description><paramref name="first"/> is bigger than <paramref name="second"/> by more than the <paramref name="maxUlps"/> tolerance.</description>
        ///     </item>
        /// </list>
        /// </returns>
        public static int CompareWithTolerance(double first, double second, long maxUlps)
        {
            if (EqualsWithTolerance(first, second, maxUlps))
            {
                return 0;
            }
            return first.CompareTo(second);
        }

        /// <summary>
        /// Compares two doubles and determines which double is bigger.
        /// </summary>
        /// <param name="first">The first value.</param>
        /// <param name="second">The second value.</param>
        /// <param name="numberOfSignificantFigures">The number of significant digits on which the values must be compared. Must be 1 or larger.</param>
        /// <returns>
        /// <list type="table">
        ///     <listheader>
        ///         <term>Return value</term>
        ///         <description>Meaning</description>
        ///     </listheader>
        ///     <item>
        ///         <term>-1</term>
        ///         <description><paramref name="first"/> is smaller than <paramref name="second"/> by more than a magnitude equal to <paramref name="numberOfSignificantFigures"/>.</description>
        ///     </item>
        ///     <item>
        ///         <term>0</term>
        ///         <description><paramref name="first"/> is equal to <paramref name="second"/> within a magnitude equal to <paramref name="numberOfSignificantFigures"/>.</description>
        ///     </item>
        ///     <item>
        ///         <term>1</term>
        ///         <description><paramref name="first"/> is bigger than <paramref name="second"/> by more than a magnitude equal to <paramref name="numberOfSignificantFigures"/>.</description>
        ///     </item>
        /// </list>
        /// </returns>
        public static int CompareWithSignificantFigures(double first, double second, int numberOfSignificantFigures)
        {
            if (EqualsWithinSignificantFigures(first, second, numberOfSignificantFigures))
            {
                return 0;
            }
            return first.CompareTo(second);
        }
    }
}
